/**
 * @author Giuseppe Mendola
 * @author Virginie Blancs
 */
package sa4.persistence

import org.squeryl.KeyedEntity
import org.squeryl.SessionFactory
import org.squeryl.Session
import org.squeryl.adapters.H2Adapter
import scala.util.matching.Regex
import org.squeryl.adapters.H2Adapter
import java.sql.Timestamp
import java.sql.Date
import org.squeryl.PrimitiveTypeMode._
import org.squeryl.dsl.ast.LogicalBoolean
import org.squeryl.annotations.OptionType

class Persistence {

  def startDB = {
    SessionFactory.concreteFactory = Some(() =>
      Session.create(
        java.sql.DriverManager.getConnection("jdbc:h2:" + System.getProperty("user.dir") + "/test"),
        new H2Adapter))
  }
  /**
   * Function that adds a new user in the database
   * @param userName	a String that contain the user name of the user.
   * @param name		a String that contain the real first name of the user.
   * @param surname		a String that contain the real surname of the user.
   * @param email		a String that contain the email of the user.
   * @param pass		a String that contain the password of the user.
   * @param birthdate	a String that contain the birthdate of the user.
   * @return			a User that represent the instance added in the database.
   */
  def newUser(userName: String, name: String, surname: String, email: String, pass: String, birthdate: java.util.Date) = {
    transaction {
      UsiBets.users.insert(new User(userName, name, surname, email, pass, birthdate, 100, 8))
    }
  }

  /**
   * Function that adds a new bet in the database.
   * @param endDate		the date limit for the bet
   * @param title 		a String with the title of the bet.
   * @param description a String that contain the description of the bet.
   */
  def newBet(title: String, creator: User, description: String, endDate: java.util.Date) {
    transaction {
      UsiBets.bets.insert(new Bet(title, creator.id, description, endDate, -1L))
    }
  }

  /**
   * Function that will return all the users in the database.
   * @return	a org.Squeryl.Query contain all the users in the database.
   */

  def setWinner(bet:Bet,choice:BetChoice){
    transaction{
      update(UsiBets.bets)(b=>
        where(b.id===bet.id)
        set(b.winnerChoice:=choice.id))
        
    }
    
  }
  
  def getWinners(bet: Bet) = {
    transaction {
      from(UsiBets.currentBets)(c =>
        where(c.bet === bet.id and c.choice.id === bet.winnerChoice)
          select (c)).toList
    }
  }
  

  def pay(bet: Bet) {
    print(getWinners(bet))
    for (s <- getWinners(bet)) {
      print(s)
      transaction {
        val user = getUser(s.user)
        user.credits = user.credits + s.possibleGain.toInt
        user.level = user.level + 1
      }
    }
  }

  def getUsers = {
    transaction {
      val allUsers = from(UsiBets.users)(a => select(a))
      allUsers
    }
  }

  /**
   * Function that return a specific user from the database.
   * @param username		a string that represent the username of the user in which we are interested.
   * @return us.head		a sa4.persitence.User object that represent the user.
   */
  def getUser(username: String) = {
    transaction {
      val us = from(UsiBets.users)(user =>
        where(user.id === username)
          select (user))
      us.head
    }
  }

  def userUnique(user: String) = {
    transaction {
      var res = from(UsiBets.users)(c =>
        where(c.id === user)
          select (c.id)).headOption

      if (res == None) {
        true
      } else {
        false
      }
    }
  }

  def mailUnique(mail: String) = {
    transaction {
      var res = from(UsiBets.users)(c =>
        where(c.email === mail)
          select (c.id)).headOption

      if (res == None) {
        true
      } else {
        false
      }
    }
  }

  /**
   * Function that check if already exists a bet with the given name in the database.
   * @param title		a String that contains the name of the bet.
   * @return true		if the the bet doesn't exists.
   * @return false		if there is already a bet with that name in the database.
   */
  def betUnique(title: String) = {
    transaction {
      var res = from(UsiBets.bets)(b =>
        where(b.title === title)
          select (b.title)).headOption
      if (res == None) {
        true
      } else {
        false
      }
    }
  }

  /**
   * Function that return a specific bet from the database by the title of the bet.
   * @param title		a String that contains the name of the bet.
   * @return us.head	a sa4.persistence.Bet object that represent the bet.
   */
  def getBet(title: String) = {
    transaction {
      val us = from(UsiBets.bets)(bet =>
        where(bet.title === title)
          select (bet))
      us.head
    }
  }

  /**
   * Function that return a specific bet from the database by the id of the bet.
   * @param id			a Long that contains the id of the bet.
   * @return us.head	a sa4.persistence.Bet object that represent the bet.
   */
  def getBetId(id: Long) = {
    transaction {
      val us = from(UsiBets.bets)(bet =>
        where(bet.id === id)
          select (bet))
      us.head
    }
  }

  /**
   * Function that return a specific choice from the database by the id of the choice.
   * @param id			a Long that contains the id of the choice.
   * @return us.head	a sa3.persistence.BetChoice that represent the choice.
   */
  def getChoiceString(s:String) = {
    transaction {
      val us = from(UsiBets.betchoices)(choice =>
        where(choice.name === s)
          select (choice))
      us.head
    }
  }
  
  
  
  def getChoice(id: Long) = {
    transaction {
      val us = from(UsiBets.betchoices)(choice =>
        where(choice.id === id)
          select (choice))
      us.head
    }
  }

  def getCurrentBet(userName: String, betId: Long) = {
    transaction {
      val us = from(UsiBets.currentBets)(current =>
        where(current.user === userName and current.bet === betId)
          select (current))
      us.head
    }
  }

  /**
   * Function that list all the active bets that are in the database.
   * @return		a scala.xml.Elem that contain all the bets
   */
  def seeBets: scala.xml.Elem = {
    <div class="content">
      { listBets }
    </div>

  }
  

  def listBets: scala.xml.Elem = {
    transaction {

      <div class="bets" id="slider2">
        {
          for (a <- UsiBets.bets)
            yield <a href={ "/bet?id=" + a.id }>
                    <div id="elementSlide">
                      <p><span>Title of the bet: { a.title }</span></p>
                      <p><span>Decription: { a.description }</span></p>
                      <p><span>End Date: { a.endDate }</span></p>
                    </div>
                  </a>
        }
      </div>
    }
  }
  def levelUp(user: User) {
    user.level = user.level + 1
  }

  /**
   * Function that create a template that display the informations of a bet.
   * @param betId	a Long that represent the id of a bet.
   * @return		scala.xml.Elem that represent the template.
   */
  def betInfo(betId: Long): scala.xml.Elem = {
    var bet = getBetId(betId)

    transaction {
      <surround with="src/main/scala/sa4/betapp/home.html" at="body">
        <div class="content">
          <p><span>Bet: { bet.title }</span></p>
          <p><span>Description: { bet.description }</span></p>
          <p><span>Ending: { bet.endDate }</span></p>
          <form class="well span8" id="addBetForm" novalidate="novalidate" method="post" handler="placeBet(sa4.Bet)">
            { getChoices(bet.title) }
            <label>Place bet</label>
            <input type="text" required="required" name="yourBet" pattern="[0-9]{1,}" class="span3"/>
            <button type="submit" class="btn btn-primary" id="placeBet">
              Place Bet
            </button>
          </form>
        </div>
      </surround>
    }
  }

  def placeWinner(betId: Long): scala.xml.Elem = {
    var bet = getBetId(betId)
    val id=betId.toString()
    transaction {
      <surround with="src/main/scala/sa4/betapp/home.html" at="body">
        <div class="content">
          <p><span>Bet: { bet.title }</span></p>
          <p><span>Description: { bet.description }</span></p>
          <p><span>Ending: { bet.endDate }</span></p>
          <form class="well span8" id="addBetForm" novalidate="novalidate" method="post" handler="setWinner(sa4.Bet)">
          
            { getChoices(bet.title) }
            <label>Set Winner</label>
            <button type="submit" class="btn btn-primary" id="placeBet">
              Winner
            </button>
          </form>
        </div>
      </surround>
    }

  }

  /**
   * Function that return all the choices for a specific bet.
   * @param betName		a String that represent the name of the bet.
   * @return			a scala.xml.Elem that contain the choices.
   */
  def getChoices(betName: String) = {
    transaction {
      <input type="text" value={betName} name="betName" readonly="readonly"/>
      <div class="betChoices">
        {
          for (a <- UsiBets.betchoices) yield if (a.betName == betName)
            <p><input type="radio" name="choice" value={ a.id.toString() } checked="checked"/><span> { a.name } -> 1 : { a.rate }</span></p>
        }
      </div>

    }
  }

  /**
   * Function that return the information of a user in a scala.xml.Elem
   * @param user		a sa4.persistence.User object that represent the user.
   * @return			a scala.xml.Elem with the information of the user.
   */
  def listUser(user: User): scala.xml.Elem = {
    transaction {
      <div class="content">
        <p><span>Username: { user.userName }</span></p>
        <p><span>Email: { user.email }</span></p>
        <p><span>Credits: { user.credits }</span></p>
        <p><span>Level: { user.level }</span></p>
        <p><span>Username: { user.userName }</span></p>
        <p><span>Birthday : { user.birthdate }</span></p>
        { listCurrentBets(user) }
      </div>
    }
  }

  def listOfAddedBets(user: User)= {
    transaction {
      <div id="slider3">

    	{
    	  for (bets<-UsiBets.bets) yield
    	  	if (user.userName==bets.createdBy){
    	    	<a class="bets" href={"/mybet?id="+bets.id}>
    			<div id="elementSlide">
    				<p><span>Title of the bet: { bets.title }</span></p>
                	<p><span>Decription: { bets.description }</span></p>
                	<p><span>End Date: { bets.endDate }</span></p>
            	</div>
                </a>
    	    }
    	  }
    	</div>
    	}
    }
  

  def listCurrentBets(user: User) = {
    transaction {
      <div id="slider1">
        {
          for (bets <- UsiBets.currentBets) yield if (user.userName == bets.user) {
            val bet = getBetId(bets.bet)
            var winner="NO Winner"
            if (bet.winnerChoice.toInt>=0){
            winner=getChoice(bet.winnerChoice).name}
            <a class="bets" href={ "/bet?id=" + bets.bet }>
              <div id="elementSlide">
                <p><span>Title of the bet: { bet.title }</span></p>
                <p><span>Decription: { bet.description }</span></p>
                <p><span>End Date: { bet.endDate }</span></p>
                <p><span>Winner: { winner }</span></p>

              </div>
            </a>
          }
        }
      </div>
    }
  }

  /**
   * Function that add a bet in the database.
   * @param user	a sa4.persistence.User object that represent the user.
   * @param bet		a sa4.persistence.Bet object that represent the bet.
   */
  def addBet(user: User, bet: Bet) = {
    transaction {
      UsiBets.currentBets.insert(new CurrentBet(user.userName, bet.id, 0, 0, null))
    }
  }

  /**
   * Function that check if a bet choice exists.
   * @param betChoice		a String that represent the name of the bet choice.
   * @return true			if it exists.
   * @return false			if it doesn't exists.
   */
  def isBetChoice(betChoice: String): Boolean = {
    transaction {
      var res = from(UsiBets.betchoices)(b =>
        where(b.name === betChoice)
          select (b))
      if (res != None) {
        return true
      } else {
        return false
      }
    }
  }

  /**
   * Function that check if a bet exists.
   * @param bet			a String that contains the name of the bet.
   * @return true		if the bet exists.
   * @return false		if the bet doesn't exists.
   */
  def isBet(bet: String): Boolean = {
    transaction {
      var res = from(UsiBets.bets)(b =>
        where(b.title === bet)
          select (b))
      if (res != None) {
        return true
      } else {
        return false
      }
    }
  }

  /**
   * Function that check if a username and a password are in the databse.
   * @param userName		a String that contain the username of a user.
   * @param pass			a Stirng that contain the password if the user.
   * @return true			if the username and password exists in the database.
   * @return false			if the username and password doesn't exists in the database.
   */
  def loginMethod(userName: String, pass: String) = {
    transaction {
      var res = from(UsiBets.users)(c =>
        where(c.id === userName and c.pass === pass)
          select (c.id)).headOption

      if (res != None) {
        true
      } else {
        false
      }
    }
  }

  /**
   * Function that gets the bets by end date.
   * @return res	a org.squeryl.Query[sa4.persistence.Bet] that represent the query for all the bet ordered by end date.
   */
  def getAllBetsOrdered() = {
    transaction {
      var res = from(UsiBets.bets)(a =>
        select(a)
          orderBy (a.endDate))
      res
    }
  }

  def updateCredits(user: User, newcredits: Int) = {
    transaction {
      update(UsiBets.users)(u => where(u.userName === user.userName)
        set (u.credits := newcredits))
    }
  }
  

  def addWin(bet: Bet) = {
    transaction {
      for (user <- UsiBets.users) {
        if (isBetted(user.userName, bet.id)) {
          var current = getCurrentBet(user.userName, bet.id)
          if(current.choice.name==bet.winnerChoice) {
            var gain = user.credits + current.possibleGain.toInt
            updateCredits(user, gain)
          }
        }
      }
      UsiBets.bets.deleteWhere(b => bet.id === b.id)
      UsiBets.currentBets.deleteWhere(current => current.bet === bet.id)
      UsiBets.betchoices.deleteWhere(choice => choice.betName === bet.title)
    }
  }

  def isBetted(userName: String, betId: Long): Boolean = {
    transaction {
      for (e <- UsiBets.currentBets) {
        if (userName == e.user && betId == e.bet) {
          return true
        }
      }
      return false
    }
  }
  
  def isCreater(userName: String, betId: Long): Boolean = {
    transaction{
      for(e<- UsiBets.bets){
        if (userName == e.createdBy && betId == e.id){
          return true
        }
      }
      return false
    }
  }


}

//Setting Relation User
class User(val userName: String, val name: String, val surname: String, val email: String, val pass: String, val birthdate: java.util.Date, var credits: Int, var level: Integer) {
  val id = userName
}

class CurrentBet(val user: String, val bet: Long, val value: Int, val possibleGain: Double, val choice: BetChoice) {
  val id = 0
}

class BetChoice(val name: String, val rate: Double, val betName: String) extends KeyedEntity[Long]{
  val id = 0L
}

//Setting Relation Bet
class Bet(val title: String, val createdBy: String, val description: String, val endDate: java.util.Date, var winnerChoice: Long) extends KeyedEntity[Long] {
  val id = 0L

  /**
   * Function that get all the bets from the database.
   * @return		a org.squeryl.Query[sa4.persistence.Bet] object that represent the query that return all the bets.
   */
  def getAllBets() = {
    from(UsiBets.bets)(a =>
      select(a))
  }

  def getRanking() = {
    from(UsiBets.users)(
      a => select(a)
        orderBy (a.credits.toInt))
  }
  /*
   * betting functions
   */

  def levelUp(user: User) = {
    transaction {
      user.level += 1
    }

    def bet(user: User, credits: String) = {
      transaction {
        user.credits -= credits.toInt
      }
    }

    def payOut(user: User, betted: Int, gain: Int) = {
      val win = betted * gain
      transaction {
        user.credits += win
      }
    }

    def getBetters(bet: Long)  = {
      transaction {
        from(UsiBets.currentBets)((b) =>
          where(b.bet === bet)
            select (b.user))
      }
    }

    def betOut(bet: Bet): Boolean = {
      val today = new java.util.Date()
      if (bet.endDate == today) {
        return true
      } else {
        return false
      }
    }

  }
}

//Schema of the database
object UsiBets extends org.squeryl.Schema {
  val users = table[User]
  on(users)(s => declare(
    s.userName is (unique),
    s.email is (unique)))
  val bets = table[Bet]
  val currentBets = table[CurrentBet]
  val betchoices = table[BetChoice]
}